package com.zzyy.study.aop;

/**
 * @auther zzyy
 * @create 2024-03-19 17:28
 */
import com.zzyy.study.annotation.AvoidRepeatSubmit;
import com.zzyy.study.service.RedisDistributedLock;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.aspectj.lang.reflect.Pointcut;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import java.lang.reflect.Method;
import java.util.UUID;


/**再需要接口防重的接口上加上AvoidRepeatSubmit注解
 * 防止重复提交的切面
 */
@Aspect
@Component
@Slf4j
public class RepeatSubmitAspect {

    @Resource
    private RedisDistributedLock redisDistributedLock;

    /**
     * 利用环绕通知进行处理重复提交问题
     * @param joinPoint
     * @return
     * @throws Throwable
     */
    //@Around("execution(public * * (..)) && @annotation(com.zzyy.study.annotation.AvoidRepeatSubmit)")
    @Around("@annotation(com.zzyy.study.annotation.AvoidRepeatSubmit)")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable
    {
        Object result = null;

        //1 容器捞鱼-AOP-反射，扫描出配置过AvoidRepeatSubmit注解的方法
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        Method method = methodSignature.getMethod();
        AvoidRepeatSubmit avoidRepeatSubmit = method.getAnnotation(AvoidRepeatSubmit.class);

        //2 获取AvoidRepeatSubmit注解上配置过的属性，锁定时间
        long lockSeconds = avoidRepeatSubmit.lockTime();

        //3 web请求参数优化处理,为了获得HttpServletRequest
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes)RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = requestAttributes.getRequest();

        //4 此处可以用token或者JSessionId
        String token = request.getHeader("token");
        String path = request.getServletPath();
        String key = token + path;
        String clientId = UUID.randomUUID().toString();

        //5 加锁，锁定多少秒，点过一次按钮就表示提交了一次，进redis生成key(好比加锁了)
        boolean isSuccess = redisDistributedLock.setLock(key, clientId, lockSeconds);

        if (isSuccess)
        {
            // 获取锁成功,认为是合法提交的请求
            log.info("tryLock success, key = [{}], clientId = [{}]", key, clientId);
            try {
                result = joinPoint.proceed();//执行进程放行
            } finally {
                //6 解锁
                redisDistributedLock.releaseLock(key, clientId);
                log.info("releaseLock success, key = [{}], clientId = [{}]", key, clientId);
                System.out.println();
            }
            return result;
        }else{
            // 获取锁失败，认为是重复提交的请求
            log.info("tryLock fail, key = [{}]", key);
            return ResponseEntity.ok("防抖启用，短时间内重复请求有点多，请稍后再试" +
                    "或者锁过期时间你设置太长");
        }
    }
}
